﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using ZXW.DotNet.Common.Basic.Logger;
using ZXW.DotNet.Common.Basic.ObjectManagement.Managers;

namespace ZXW.DotNet.Common.Basic.Network.Tcp
{
    /// <summary>
    /// TCPProxy 代理类
    /// </summary>
    public class TCPProxy
    {
        // Fields
        private IAgileLogger agileLogger;
        private AgileTcpListener agileTcpListener;
        private int clientBufferSize;
        ///// <summary>
        ///// 创建代理之前
        ///// </summary>
        //private CbGeneric<TCPProxy> OnBeforeConnection;
        /// <summary>
        /// 创建连接之后
        /// </summary>
        private CbGeneric<ConnectionPair> OnConnectionPairCreated;
        private CbGeneric<IPEndPoint> OnConnectionPairDisposed;
        private ObjectManager<IPEndPoint, ConnectionPair> connetionPairManager;
        private CbGeneric<ConnectionPair> OnDataTransfered;
        private int port;
        private int proxyBufferSize;
        private string serverIP;
        private int serverPort;
        private int writeTimeoutInMSecs;


        #region 新添加 建立连接前数据
        //private bool _continue = true;

        ///// <summary>
        ///// 是否继续
        ///// </summary>
        //public bool Continue
        //{
        //    get { return _continue; }
        //    set { _continue = value; }
        //}

        ///// <summary>
        ///// Event:创建连接之前
        ///// 注意:建立此程序的代理后必须设置 Continue属性为真才能继续建立代理
        ///// </summary>
        //public event CbGeneric<TCPProxy> BeforeConnection
        //{
        //    add
        //    {
        //        CbGeneric<TCPProxy> generic2;
        //        CbGeneric<TCPProxy> OnBeforeConnection = this.OnBeforeConnection;
        //        do
        //        {
        //            generic2 = OnBeforeConnection;
        //            CbGeneric<TCPProxy> generic3 = (CbGeneric<TCPProxy>)Delegate.Combine(generic2, value);
        //            OnBeforeConnection = Interlocked.CompareExchange<CbGeneric<TCPProxy>>(ref this.OnBeforeConnection, generic3, generic2);
        //        }
        //        while (OnBeforeConnection != generic2);
        //    }
        //    remove
        //    {
        //        CbGeneric<TCPProxy> generic2;
        //        CbGeneric<TCPProxy> OnBeforeConnection = this.OnBeforeConnection;
        //        do
        //        {
        //            generic2 = OnBeforeConnection;
        //            CbGeneric<TCPProxy> generic3 = (CbGeneric<TCPProxy>)Delegate.Remove(generic2, value);
        //            OnBeforeConnection = Interlocked.CompareExchange<CbGeneric<TCPProxy>>(ref this.OnBeforeConnection, generic3, generic2);
        //        }
        //        while (OnBeforeConnection != generic2);
        //    }
        //}
        
        #endregion

        /// <summary>
        /// Event:创建连接完成
        /// </summary>
        public event CbGeneric<ConnectionPair> ConnectionPairCreated
        {
            add
            {
                CbGeneric<ConnectionPair> generic2;
                CbGeneric<ConnectionPair> OnConnectionPairCreated = this.OnConnectionPairCreated;
                do
                {
                    generic2 = OnConnectionPairCreated;
                    CbGeneric<ConnectionPair> generic3 = (CbGeneric<ConnectionPair>)Delegate.Combine(generic2, value);
                    OnConnectionPairCreated = 
                        Interlocked.CompareExchange<CbGeneric<ConnectionPair>>(ref this.OnConnectionPairCreated, generic3, generic2);
                }
                while (OnConnectionPairCreated != generic2);
            }
            remove
            {
                CbGeneric<ConnectionPair> generic2;
                CbGeneric<ConnectionPair> OnConnectionPairCreated = this.OnConnectionPairCreated;
                do
                {
                    generic2 = OnConnectionPairCreated;
                    CbGeneric<ConnectionPair> generic3 = (CbGeneric<ConnectionPair>)Delegate.Remove(generic2, value);
                    OnConnectionPairCreated = 
                        Interlocked.CompareExchange<CbGeneric<ConnectionPair>>(ref this.OnConnectionPairCreated, generic3, generic2);
                }
                while (OnConnectionPairCreated != generic2);
            }
        }

        /// <summary>
        /// Event:连接断开
        /// </summary>
        public event CbGeneric<IPEndPoint> ConnectionPairDisposed
        {
            add
            {
                CbGeneric<IPEndPoint> generic2;
                CbGeneric<IPEndPoint> OnConnectionPairDisposed = this.OnConnectionPairDisposed;
                do
                {
                    generic2 = OnConnectionPairDisposed;
                    CbGeneric<IPEndPoint> generic3 = (CbGeneric<IPEndPoint>)Delegate.Combine(generic2, value);
                    OnConnectionPairDisposed = 
                        Interlocked.CompareExchange<CbGeneric<IPEndPoint>>(ref this.OnConnectionPairDisposed, generic3, generic2);
                }
                while (OnConnectionPairDisposed != generic2);
            }
            remove
            {
                CbGeneric<IPEndPoint> generic2;
                CbGeneric<IPEndPoint> OnConnectionPairDisposed = this.OnConnectionPairDisposed;
                do
                {
                    generic2 = OnConnectionPairDisposed;
                    CbGeneric<IPEndPoint> generic3 = (CbGeneric<IPEndPoint>)Delegate.Remove(generic2, value);
                    OnConnectionPairDisposed = 
                        Interlocked.CompareExchange<CbGeneric<IPEndPoint>>(ref this.OnConnectionPairDisposed, generic3, generic2);
                }
                while (OnConnectionPairDisposed != generic2);
            }
        }

        /// <summary>
        /// Event:收到数据
        /// </summary>
        public event CbGeneric<ConnectionPair> DataTransfered
        {
            add
            {
                CbGeneric<ConnectionPair> generic2;
                CbGeneric<ConnectionPair> OnDataTransfered = this.OnDataTransfered;
                do
                {
                    generic2 = OnDataTransfered;
                    CbGeneric<ConnectionPair> generic3 = (CbGeneric<ConnectionPair>)Delegate.Combine(generic2, value);
                    OnDataTransfered = 
                        Interlocked.CompareExchange<CbGeneric<ConnectionPair>>(ref this.OnDataTransfered, generic3, generic2);
                }
                while (OnDataTransfered != generic2);
            }
            remove
            {
                CbGeneric<ConnectionPair> generic2;
                CbGeneric<ConnectionPair> OnDataTransfered = this.OnDataTransfered;
                do
                {
                    generic2 = OnDataTransfered;
                    CbGeneric<ConnectionPair> generic3 = (CbGeneric<ConnectionPair>)Delegate.Remove(generic2, value);
                    OnDataTransfered = 
                        Interlocked.CompareExchange<CbGeneric<ConnectionPair>>(ref this.OnDataTransfered, generic3, generic2);
                }
                while (OnDataTransfered != generic2);
            }
        }

        // Methods
        public TCPProxy()
        {
            this.connetionPairManager = new ObjectManager<IPEndPoint, ConnectionPair>();
            this.agileLogger = new EmptyAgileLogger();
            this.proxyBufferSize = 0x2000;
            this.clientBufferSize = 0x2000;
            this.writeTimeoutInMSecs = -1;
        }

        private void agileTcpListener_TcpConnectionEstablished(NetworkStream stream, EndPoint romoteEP)
        {
            Exception exception;
            stream.WriteTimeout = this.writeTimeoutInMSecs;
            NetworkStream proxy = null;
            AgileTcpClient client = null;
            try
            {
                client = new AgileTcpClient(this.serverIP, this.serverPort);
                proxy = client.GetNetworkStream();
            }
            catch (Exception exception1)
            {
                exception = exception1;
                stream.Close();
                stream.Dispose();
                this.agileLogger.Log(exception, "TCPProxy.agileTcpListener_TcpConnectionEstablished 与真实服务器建立tcp连接时", ErrorLevel.Standard);
                return;
            }
            ConnectionPair pair = new ConnectionPair(stream, proxy, (IPEndPoint)romoteEP, this.proxyBufferSize, this.clientBufferSize, client.Port);
            try
            {
                this.connetionPairManager.Add(pair.ClientIPE, pair);
                if (this.OnConnectionPairCreated != null)
                {
                    this.OnConnectionPairCreated(pair);
                }
                pair.ClientStream.BeginRead(pair.ClientBuffer, 0, pair.ClientBuffer.Length, new AsyncCallback(this.OnClientData), pair);
                pair.ProxyStream.BeginRead(pair.ProxyBuffer, 0, pair.ProxyBuffer.Length, new AsyncCallback(this.OnRealServerData), pair);
            }
            catch (Exception exception2)
            {
                exception = exception2;
                this.DisposeConnectionPair(pair);
                this.agileLogger.Log(exception, "TCPProxy.agileTcpListener_TcpConnectionEstablished 从连接上接收数据时", ErrorLevel.Standard);
            }
        }


        public void CloseConnection(IPEndPoint clientIPE)
        {
            ConnectionPair pair = this.connetionPairManager.Get(clientIPE);
            if (pair != null)
            {
                this.DisposeConnectionPair(pair);
            }
        }


        private void DisposeConnectionPair(ConnectionPair pair)
        {
            this.connetionPairManager.Remove(pair.ClientIPE);
            pair.Dispose();
            if (this.OnConnectionPairDisposed != null)
            {
                this.OnConnectionPairDisposed(pair.ClientIPE);
            }
        }

        public IList<IPEndPoint> GetClientList()
        {
            return this.connetionPairManager.GetKeyList();
        }

        public ConnectionPair GetConnectionPair(IPEndPoint clientIPE)
        {
            return this.connetionPairManager.Get(clientIPE);
        }

        public void Initialize()
        {
            if (this.agileTcpListener != null)
            {
                this.agileTcpListener.Stop();
            }
            this.agileTcpListener = new AgileTcpListener(this.port);
            this.agileTcpListener.TcpConnectionEstablished +=
                new CbStrongNetworkStream(this.agileTcpListener_TcpConnectionEstablished);          
            this.agileTcpListener.Start();
        }

        private void OnClientData(IAsyncResult ar)
        {
            ConnectionPair asyncState = (ConnectionPair)ar.AsyncState;
            try
            {
                int count = asyncState.ClientStream.EndRead(ar);
                if (count == 0)
                {
                    this.DisposeConnectionPair(asyncState);
                }
                else
                {
                    asyncState.ProxyStream.Write(asyncState.ClientBuffer, 0, count);
                    asyncState.BytesFromClient += (uint)count;
                    if (this.OnDataTransfered != null)
                    {
                        this.OnDataTransfered(asyncState);
                    }
                    asyncState.ClientStream.BeginRead(asyncState.ClientBuffer, 0, asyncState.ClientBuffer.Length, new AsyncCallback(this.OnClientData), asyncState);
                }
            }
            catch (Exception)
            {
                this.DisposeConnectionPair(asyncState);
            }
        }

        private void OnRealServerData(IAsyncResult ar)
        {
            ConnectionPair asyncState = (ConnectionPair)ar.AsyncState;
            try
            {
                int count = asyncState.ProxyStream.EndRead(ar);
                if (count == 0)
                {
                    this.DisposeConnectionPair(asyncState);
                }
                else
                {
                    asyncState.ClientStream.Write(asyncState.ProxyBuffer, 0, count);
                    asyncState.BytesFromServer += (uint)count;
                    if (this.OnDataTransfered != null)
                    {
                        this.OnDataTransfered(asyncState);
                    }
                    asyncState.ProxyStream.BeginRead(asyncState.ProxyBuffer, 0, asyncState.ProxyBuffer.Length, new AsyncCallback(this.OnRealServerData), asyncState);
                }
            }
            catch (Exception exception)
            {
                this.DisposeConnectionPair(asyncState);
                this.agileLogger.Log(exception, "TCPProxy.OnRealServerData", ErrorLevel.Standard);
            }

        }

        // Properties
        public IAgileLogger AgileLogger
        {
            set
            {
                this.agileLogger = value;
            }
        }

        public int ClientBufferSize
        {
            get
            {
                return this.clientBufferSize;
            }
            set
            {
                this.clientBufferSize = value;
            }
        }

        public int ConnectionCount
        {
            get
            {
                return this.connetionPairManager.Count;
            }
        }

        public int Port
        {
            get
            {
                return this.port;
            }
            set
            {
                this.port = value;
            }
        }

        public int ProxyBufferSize
        {
            get
            {
                return this.proxyBufferSize;
            }
            set
            {
                this.proxyBufferSize = value;
            }
        }

        public string ServerIP
        {
            get
            {
                return this.serverIP;
            }
            set
            {
                this.serverIP = value;
            }
        }

        public int ServerPort
        {
            get
            {
                return this.serverPort;
            }
            set
            {
                this.serverPort = value;
            }
        }

        public int WriteTimeoutInMSecs
        {
            get
            {
                return this.writeTimeoutInMSecs;
            }
            set
            {
                this.writeTimeoutInMSecs = value;
            }
        }

    }
}
